package com.icesoft.base.manager.service.security;


import cn.hutool.core.io.IoUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.icesoft.base.manager.base.BaseService;
import com.icesoft.base.manager.config.UrlConstant;
import com.icesoft.base.manager.entity.security.RelSysRoleAuthority;
import com.icesoft.base.manager.entity.security.SysApiAuthority;
import com.icesoft.base.manager.entity.security.SysApiAuthorityGroup;
import com.icesoft.base.manager.entity.security.SysMenu;
import com.icesoft.base.manager.model.SysApiAuthorityDTO;
import com.icesoft.core.common.helper.TreeBean;
import com.icesoft.core.common.util.JsonUtil;
import com.icesoft.core.common.util.UuidUtil;
import com.icesoft.core.dao.criteria.QueryBuilder;
import com.icesoft.core.web.helper.PathUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author XHH
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysApiAuthorityGroupService extends BaseService<SysApiAuthorityGroup> {

    private RelSysRoleAuthorityService relSysRoleAuthorityService;
    private SysApiAuthorityService sysApiAuthorityService;
    private RequestMappingHandlerMapping mapping;
    private SysMenuService sysMenuService;

    public SysApiAuthorityGroup findByName(String name) {
        return unique(QueryBuilder.get("name", name));
    }

    public List<SysApiAuthorityGroup> findByPreUrl(String preUrl) {
        return find(QueryBuilder.get("preUrl", preUrl));
    }

    public List<TreeBean> tree(int roleId) {
        List<RelSysRoleAuthority> roleAuthorities = relSysRoleAuthorityService.findByRoleId(roleId);
        List<TreeBean> trees = new ArrayList<>();
        List<SysApiAuthorityGroup> groups = findAll();
        groups.forEach(sysApiAuthorityGroup -> trees.add(new TreeBean("-" + sysApiAuthorityGroup.getId(), "0", sysApiAuthorityGroup.getName(), true)));
        List<SysApiAuthority> authorities = sysApiAuthorityService.findAll();
        authorities.forEach(simpleAuthority -> {
            TreeBean treeBean = new TreeBean("" + simpleAuthority.getId(), "-" + simpleAuthority.getGroupId(), simpleAuthority.getName() + "(" + simpleAuthority.getAuthority() + ")");
            roleAuthorities.forEach(relSysRoleAuthority -> {
                if (relSysRoleAuthority.getAuthorityId() == simpleAuthority.getId()) {
                    treeBean.setChecked(true);
                }
            });
            trees.add(treeBean);
        });
        return trees;
    }

    static Map<String, String> apiDesc;
    static {
        try {
            String json = IoUtil.readUtf8(PathUtil.getPathInputStream("config/apiDesc.json"));
            apiDesc = JsonUtil.toObject(json, new TypeReference<Map<String, String>>() {
            });
        } catch (IOException e) {
            apiDesc = new HashMap<>();
        }
    }
    public List<TreeBean> urlTree() {

        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        List<String> urlList = new ArrayList<>();
        for (RequestMappingInfo info : map.keySet()) {
            // 获取url的Set集合，一个方法可能对应多个url
            Set<String> patterns = info.getPatternsCondition().getPatterns();
            urlList.addAll(patterns);
        }
        log.debug("urlList:{}", urlList);
        List<String> authorities = sysApiAuthorityService.findAll().stream().map(SysApiAuthority::getAuthority).collect(Collectors.toList());
        urlList.removeAll(authorities);
        log.debug("left urlList:{}", urlList);
        List<TreeBean> trees = new ArrayList<>();

        filterSysMenu(urlList, trees);
        filterGroups(urlList, trees);
        TreeBean treeBean = new TreeBean(UuidUtil.get32UUID(), "", "其他接口", true);
        treeBean.setOpen(false);
        trees.add(treeBean);
        for (String url : urlList) {
            String key = url.substring(url.lastIndexOf("/") + 1);
            String name = apiDesc.getOrDefault(url,key);
            trees.add(new TreeBean(url, treeBean.getId(), String.format("%s(%s)", name, url), false, false));
        }
        return trees;
    }

    private void filterSysMenu(List<String> urlList, List<TreeBean> trees) {
        List<SysMenu> sysMenus = sysMenuService.findAll();
        sysMenus.forEach(menu -> {
            if (StringUtils.isBlank(menu.getUrl())) {
                return;
            }
            String preUrl = menu.getUrl().substring(0, menu.getUrl().lastIndexOf("/") + 1);
            preUrl = PathUtil.appendPathStart(preUrl);
            Map<String, String> basicUrlMap = UrlConstant.getUrlNameMap();
            boolean emptyUrl = true;
            String pid = menu.getId().toString();
            Iterator<String> iterator = urlList.iterator();
            while (iterator.hasNext()) {
                String url = iterator.next();
                if (url.startsWith(preUrl)) {
                    String key = url.substring(preUrl.length());
                    emptyUrl = false;
                    String name = apiDesc.getOrDefault(url,basicUrlMap.getOrDefault(key, key));
                    trees.add(new TreeBean(url, pid, String.format("%s(%s)", name, key), false, true));
                    iterator.remove();
                }
            }
            if (!emptyUrl) {
                TreeBean treeBean = new TreeBean(pid, preUrl, String.format("%s(%s)", menu.getTitle(), preUrl), true);
                treeBean.setOpen(true);
                trees.add(treeBean);
            }
        });
    }

    private static final Map<String, String> API_GROUP = new LinkedHashMap<>();

    static {
        API_GROUP.put("/system/", "系统相关接口");
        API_GROUP.put("/manager/", "管理相关权限");
        API_GROUP.put("/security/", "超管相关权限");
    }

    private void filterGroups(List<String> urlList, List<TreeBean> trees) {
        for (Map.Entry<String, String> entry : API_GROUP.entrySet()) {
            TreeBean treeBean = new TreeBean(UuidUtil.get32UUID(), entry.getKey(), String.format("%s(%s)", entry.getValue(), entry.getKey()), true);
            treeBean.setOpen(true);
            trees.add(treeBean);
            Iterator<String> iterator = urlList.iterator();
            while (iterator.hasNext()) {
                String url = iterator.next();
                if (url.startsWith(entry.getKey())) {
                    String key = url.substring(entry.getKey().length());
                    String name = apiDesc.getOrDefault(url,key);
                    trees.add(new TreeBean(url, treeBean.getId(), String.format("%s(%s)", name, url), false, false));
                    iterator.remove();
                }
            }
        }
    }

    public List<SysApiAuthority> createByGroup(List<SysApiAuthorityDTO> sysApiAuthorityDTOList) {
        List<SysApiAuthority> list = new ArrayList<>(sysApiAuthorityDTOList.size());
        for (SysApiAuthorityDTO apiAuthorityDTO : sysApiAuthorityDTOList) {
            String preUrl = apiAuthorityDTO.getAuthority().substring(0, apiAuthorityDTO.getAuthority().lastIndexOf("/") + 1);
            if (StringUtils.isBlank(apiAuthorityDTO.getGroupName())) {
                apiAuthorityDTO.setGroupName(apiAuthorityDTO.getAuthority().substring(apiAuthorityDTO.getAuthority().indexOf("/"), apiAuthorityDTO.getAuthority().lastIndexOf("/") + 1));
            }
            SysApiAuthorityGroup sysApiAuthorityGroup = findByName(apiAuthorityDTO.getGroupName());
            if (sysApiAuthorityGroup == null) {
                List<SysApiAuthorityGroup> groups = findByPreUrl(preUrl);
                sysApiAuthorityGroup = groups.isEmpty() ? null : groups.get(0);
            }
            if (sysApiAuthorityGroup == null) {
                sysApiAuthorityGroup = new SysApiAuthorityGroup(preUrl, apiAuthorityDTO.getGroupName());
                create(sysApiAuthorityGroup);
            }
            list.add(new SysApiAuthority(apiAuthorityDTO.getName(), sysApiAuthorityGroup.getId(), apiAuthorityDTO.getAuthority()));
        }
        sysApiAuthorityService.create(list);
        return list;
    }
}
